(function(){
var Buffer=function()
	{
	this.Data=[];
	};
Buffer.prototype={
Byte:function(Value)
	{
	this.Data.push(Value);
	},
Short:function(Value)
	{
	if(Value>=0)	this.UShort(Value);
	else	{
		Value=-Value;
		this.Data.push(Value&255);
		this.Data.push((Value>>8)|(1<<7));
		}
	},
UShort:function(Value)
	{
	this.Data.push(Value&255);
	this.Data.push(Value>>8);
	},
Int:function(Value)
	{
	if(Value>=0)	this.UInt(Value);
	else	{
		Value=-Value;
		this.UShort(Value&65535);
		this.UShort((Value>>16)|(1<<15));
		}
	},
UInt:function(Value)
	{
	this.UShort(Value&65535);
	this.UShort(Value>>16);
	},
UShortString:function(Value)
	{
	if(Value.length<65536)
		{
		this.UShort(Value.length);
		if(Value.length)	this.PushString(Value,fastCSharp.ThisFunction(this,this.UShort));
		}
	},
String:function(Value)
	{
	if(Value.length<(1<<15))
		{
		if(Value.length)	this.UShortString(Value);
		}
	else	{
		this.UShort((Value.length&65535)|(1<<15));
		this.UShort(Value.length>>15);
		this.PushString(Value,fastCSharp.ThisFunction(this,Value.length<65536?this.UShort:this.UInt));
		}
	},
PushString:function(Value,PushLength)
	{
	for(var Data=[],Index=0;Index-Value.length;)
		{
		for(Data.length=0;Index-Value.length;++Index)
			{
			var Code=Value.charCodeAt(Index);
			if(Code>=256)	break;
			Data.push(Code);
			}
		PushLength(Data.length);
		this.Data=this.Data.concat(Data);
		if(Index-Value.length)
			{
			for(Data.length=0;Index-Value.length;++Index)
				{
				var Code=Value.charCodeAt(Index);
				if(Code<256)	break;
				Data.push(Code&255);
				Data.push(Code>>8);
				}
			PushLength(Data.length>>1);
			this.Data=this.Data.concat(Data);
			}
		}
	},
StringByte:function(Value)
	{
	for(var Index=0;Index-Value.length;this.Data.push(Value.charCodeAt(Index++)));
	},
GetBlob:function()
	{
	return new Blob([new Uint8Array(this.Data).buffer]);
	}
		};
var Socket=function()
	{
	this.GetArgument(arguments[0],{Url:'/',DataType:'arraybuffer',OnOpen:null,OnClose:null,OnError:null,OnData:null,OnString:null,AutoTimeout:0,PingData:null,PingTimeout:50*1000},1);
	if(this.Url.indexOf('://')==-1)
		{
		if(this.Url.substring(0,2)=='//')	this.Url='ws'+location.protocol.substring(4)+this.Url;
		else	this.Url='ws'+location.protocol.substring(4)+'//'+location.hostname+(this.Url.chatAt(0)=='/'?this.Url:('/'+this.Url));
		}
	this.Identity=this.IsQueue=0;
	this.Queue=[];
	this.OnOpen=fastCSharp.Event().Add(this.OnOpen);
	this.OnClose=fastCSharp.Event().Add(this.OnClose);
	this.OnError=fastCSharp.Event().Add(this.OnError);
	this.OnData=fastCSharp.Event().Add(this.OnData);
	this.OnString=fastCSharp.Event().Add(this.OnString);
	if(this.PingData)
		{
		(this.PingBuffer=new Buffer()).Data=this.PingData;
		this.PingTime=(new Date).AddMilliseconds(this.PingTimeout);
		setTimeout(fastCSharp.ThisFunction(this,this.Ping),this.PingTimeout);
		}
	if(this.AutoTimeout)	this.Create();
	};
(fastCSharp.Functions.Socket=Socket).Inherit(fastCSharp.BaseFunction,{
Create:function()
	{
	if(!this.IsClose&&!this.IsOpen)
		{
		++this.Identity;
		this.IsOpen=1;
		this.Socket=new WebSocket(this.Url);
		this.Socket.binaryType=this.DataType;
		this.Socket.onopen=fastCSharp.ThisFunction(this,this.Open,[this.Identity]);
		this.Socket.onclose=fastCSharp.ThisFunction(this,this.Close,[this.Identity]);
		this.Socket.onerror=fastCSharp.ThisFunction(this,this.Error,[this.Identity]);
		this.Socket.onmessage=fastCSharp.ThisFunction(this,this.Message,[this.Identity]);
		}
	},
CheckIdentity:function(Values)
	{
	for(var Index=Values.length;Index;)	if(Values[--Index]==this.Identity)	return 1;
	},
CloseSocket:function(IsClose)
	{
	++this.Identity;
	this.IsOpen=0;
	if(!IsClose&&this.Socket)	this.Socket.close();
	this.Socket=null;
	if(this.AutoTimeout)	setTimeout(fastCSharp.ThisFunction(this,this.Create),this.AutoTimeout);
	},
Ping:function()
	{
	var Timeout=this.PingTime.getTime()-(new Date).getTime();
	if(Timeout<=0)
		{
		setTimeout(fastCSharp.ThisFunction(this,this.Ping),this.PingTimeout);
		this.Send(this.PingBuffer,1);
		}
	else	setTimeout(fastCSharp.ThisFunction(this,this.Ping),Timeout+1);
	},
Open:function()
	{
	if(this.CheckIdentity(arguments))
		{
		this.IsOpen=2;
		this.PingTime=(new Date).AddMilliseconds(this.PingTimeout);
		this.OnOpen(this);
		++this.IsQueue;
		setTimeout(fastCSharp.ThisFunction(this,this.SendQueue),500);
		}
	},
Close:function()
	{
	if(arguments.length==0)
		{
		this.IsClose=1;
		this.CloseSocket();
		}
	else	{
		if(this.CheckIdentity(arguments))	this.CloseSocket(1);
		this.OnClose(this);
		}
	},
Error:function()
	{
	if(this.CheckIdentity(arguments))	this.CloseSocket();
	this.OnError(this);
	},
Message:function(Data)
	{
	if(this.CheckIdentity(arguments))
		{
		if(typeof(Data.data)=='string')	this.OnString(Data.data);
		else	this.OnData(Data.data);
		}
	},
SendQueue:function()
	{
	if(!this.IsClose&&--this.IsQueue==0&&this.IsOpen==2)
		{
		var Queue=this.Queue;
		this.Queue=[];
		for(var Index=0;Index-Queue.length;++Index)
			{
			var Value=Queue[Index];
			this.Socket.send(Value.GetBlob?Value.GetBlob():Value);
			}
		this.PingTime=(new Date).AddMilliseconds(this.PingTimeout);
		}
	},
Send:function(Value,IsPing)
	{
	if(!this.IsClose)
		{
		if(this.IsOpen==2)
			{
			if(this.IsQueue)
				{
				if(!IsPing)	this.Queue.push(Value);
				}
			else	{
				this.Socket.send(Value.GetBlob?Value.GetBlob():Value);
				this.PingTime=(new Date).AddMilliseconds(this.PingTimeout);
				}
			}
		else if(!IsPing)
			{
			this.Queue.push(Value);
			if(!this.Socket&&!this.AutoTimeout)	this.Create();
			}
		}
	},
CreateBuffer:function()	{	return new Buffer();	}
		});
})();